<template>
  <div class="image-compress">
    <el-row :gutter="20">
      <!-- 上传区域 -->
      <el-col :span="24" class="mb-4">
        <el-card>
          <template #header>
            <div class="card-header">
              <el-icon color="#9C27B0">
                <picture />
              </el-icon>
              <span class="ml-2">图片压缩工具</span>
            </div>
          </template>

          <el-upload
            class="upload-area"
            drag
            multiple
            accept="image/*"
            :auto-upload="false"
            :on-change="handleFilesUpload"
            :limit="10"
          >
            <template #trigger>
              <el-icon size="24" class="mb-2">
                <upload-filled />
              </el-icon>
              <div class="upload-text">拖拽图片到此处或点击选择</div>
              <div class="upload-tip">支持jpg、png、gif等格式，最多10张图片</div>
            </template>
          </el-upload>
        </el-card>
      </el-col>

      <!-- 压缩设置 -->
      <el-col :span="24" class="mb-4">
        <el-card class="settings-card">
          <template #header>
            <div class="card-header">
              <el-icon color="#409EFF">
                <setting />
              </el-icon>
              <span class="ml-2">压缩设置</span>
            </div>
          </template>

          <el-form label-position="top" class="settings-form">
            <el-form-item label="压缩质量">
              <div class="slider-with-input">
                <el-slider v-model="quality" :min="0" :max="100" :step="1" />
                <el-input-number
                  v-model="quality"
                  :min="0"
                  :max="100"
                  :step="1"
                  size="small"
                  class="quality-input"
                />
              </div>
            </el-form-item>

            <el-form-item label="最大宽度">
              <div class="slider-with-input">
                <el-slider v-model="maxWidth" :min="100" :max="4096" :step="100" />
                <el-input-number
                  v-model="maxWidth"
                  :min="100"
                  :max="4096"
                  :step="100"
                  size="small"
                  class="width-input"
                />
              </div>
            </el-form-item>

            <el-form-item>
              <el-checkbox v-model="keepRatio">保持宽高比</el-checkbox>
            </el-form-item>

            <el-form-item label="输出格式">
              <el-select v-model="outputFormat" class="w-full">
                <el-option
                  v-for="option in formatOptions"
                  :key="option.value"
                  :label="option.text"
                  :value="option.value"
                />
              </el-select>
            </el-form-item>

            <el-button
              type="primary"
              class="w-full mt-4"
              :loading="compressing"
              :disabled="!imageList.length"
              @click="compressAll"
            >
              开始压缩
            </el-button>
          </el-form>
        </el-card>
      </el-col>

      <!-- 图片列表 -->
      <el-col :xs="24" :sm="24" :md="16">
        <el-card v-if="imageList.length">
          <template #header>
            <div class="card-header">
              <el-icon color="#67C23A">
                <document />
              </el-icon>
              <span class="ml-2">处理列表</span>
              <div class="flex-grow" />
              <el-button type="danger" @click="clearAll">清空列表</el-button>
            </div>
          </template>

          <el-scrollbar max-height="600px">
            <div class="image-list">
              <div v-for="(image, index) in imageList" :key="index" class="image-item">
                <el-image :src="image.preview" fit="contain" class="preview-image" />
                <div class="image-info">
                  <div class="file-name">
                    {{ image.file.name }}
                  </div>
                  <div class="file-size">
                    原始大小: {{ formatSize(image.originalSize) }}
                    <template v-if="image.compressedSize">
                      | 压缩后: {{ formatSize(image.compressedSize) }} | 压缩率:
                      {{ getCompressionRate(image) }}%
                    </template>
                  </div>
                  <el-progress v-if="image.processing" :percentage="50" :indeterminate="true" />
                </div>
                <div class="image-actions">
                  <el-button
                    v-if="image.compressedUrl"
                    type="primary"
                    link
                    :icon="Download"
                    @click="downloadImage(image)"
                  />
                </div>
              </div>
            </div>
          </el-scrollbar>

          <div class="text-center mt-4">
            <el-button type="success" :disabled="!hasCompressedImages" @click="downloadAll">
              <el-icon class="mr-2">
                <folder />
              </el-icon>
              打包下载
            </el-button>
          </div>
        </el-card>
      </el-col>
    </el-row>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue';
import { ElMessage } from 'element-plus';
import JSZip from 'jszip';
import { saveAs } from 'file-saver';
import {
  Picture,
  Setting,
  Document,
  Download,
  UploadFilled,
  Folder
} from '@element-plus/icons-vue';

const files = ref([]);
const imageList = ref([]);
const quality = ref(80);
const maxWidth = ref(1920);
const keepRatio = ref(true);
const outputFormat = ref('jpeg');
const compressing = ref(false);

const formatOptions = [
  {
    text: 'JPEG',
    value: 'jpeg'
  },
  {
    text: 'PNG',
    value: 'png'
  },
  {
    text: 'WEBP',
    value: 'webp'
  }
];

const hasCompressedImages = computed(() => imageList.value.some(img => img.compressedUrl));

const handleFilesUpload = file => {
  if (!file.raw.type.startsWith('image/')) return;

  const reader = new FileReader();
  reader.onload = e => {
    imageList.value.push({
      file: file.raw,
      originalSize: file.raw.size,
      preview: e.target.result,
      processing: false,
      compressedUrl: null,
      compressedSize: null
    });
  };
  reader.readAsDataURL(file.raw);
};

const compressAll = async () => {
  if (compressing.value) return;
  compressing.value = true;

  try {
    for (const image of imageList.value) {
      if (image.compressedUrl) continue;
      await compressImage(image);
    }
    ElMessage.success('所有图片压缩完成');
  } catch (error) {
    ElMessage.error('压缩过程中出现错误');
  } finally {
    compressing.value = false;
  }
};

const compressImage = async image => {
  image.processing = true;

  try {
    const img = new Image();
    img.src = image.preview;
    await new Promise(resolve => {
      img.onload = resolve;
    });

    // 计算新的尺寸
    let newWidth = img.width;
    let newHeight = img.height;

    if (newWidth > maxWidth.value) {
      newWidth = maxWidth.value;
      if (keepRatio.value) {
        newHeight = (img.height * maxWidth.value) / img.width;
      }
    }

    // 创建canvas进行压缩
    const canvas = document.createElement('canvas');
    canvas.width = newWidth;
    canvas.height = newHeight;

    const ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0, newWidth, newHeight);

    // 转换为blob
    const blob = await new Promise(resolve => {
      canvas.toBlob(resolve, `image/${outputFormat.value}`, quality.value / 100);
    });

    image.compressedUrl = URL.createObjectURL(blob);
    image.compressedSize = blob.size;
  } catch (error) {
    ElMessage.error(`压缩 ${image.file.name} 失败`);
  } finally {
    image.processing = false;
  }
};

const downloadImage = image => {
  if (!image.compressedUrl) return;

  const link = document.createElement('a');
  link.href = image.compressedUrl;
  link.download = getOutputFileName(image.file.name);
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
};

const downloadAll = async () => {
  try {
    const zip = new JSZip();
    const promises = imageList.value
      .filter(img => img.compressedUrl)
      .map(async img => {
        const blob = await fetch(img.compressedUrl).then(r => r.blob());
        zip.file(getOutputFileName(img.file.name), blob);
      });

    await Promise.all(promises);
    const content = await zip.generateAsync({ type: 'blob' });
    saveAs(content, 'compressed_images.zip');

    ElMessage.success('打包下载成功');
  } catch (error) {
    ElMessage.error('打包下载失败');
  }
};

const clearAll = () => {
  imageList.value.forEach(img => {
    if (img.compressedUrl) {
      URL.revokeObjectURL(img.compressedUrl);
    }
  });
  imageList.value = [];
  files.value = [];
};

const getOutputFileName = originalName => {
  const ext = outputFormat.value === 'jpeg' ? 'jpg' : outputFormat.value;
  return `${originalName.replace(/\.[^/.]+$/, '')}_compressed.${ext}`;
};

const formatSize = bytes => {
  if (bytes === 0) return '0 B';
  const k = 1024;
  const sizes = ['B', 'KB', 'MB', 'GB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
};

const getCompressionRate = image => {
  if (!image.compressedSize) return 0;
  const rate = ((image.originalSize - image.compressedSize) / image.originalSize) * 100;
  return rate.toFixed(1);
};
</script>

<style scoped>
.image-compress {
  max-width: 1400px;
  margin: 0 auto;
  padding: 20px;
}

.card-header {
  display: flex;
  align-items: center;
  gap: 8px;
}

.ml-2 {
  margin-left: 0.5rem;
}

.mr-2 {
  margin-right: 0.5rem;
}

.mt-4 {
  margin-top: 1rem;
}

.mb-2 {
  margin-bottom: 0.5rem;
}

.w-full {
  width: 100%;
}

.flex-grow {
  flex-grow: 1;
}

.text-center {
  text-align: center;
}

.upload-area {
  text-align: center;
  padding: 40px;
  border: 2px dashed var(--el-border-color);
  border-radius: 6px;
}

.upload-text {
  font-size: 16px;
  color: var(--el-text-color-primary);
  margin: 10px 0;
}

.upload-tip {
  font-size: 12px;
  color: var(--el-text-color-secondary);
}

.image-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.image-item {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px;
  border-radius: 8px;
  background: var(--el-fill-color-light);
}

.preview-image {
  width: 100px;
  height: 100px;
  object-fit: contain;
  border-radius: 4px;
}

.image-info {
  flex: 1;
}

.file-name {
  font-weight: 500;
  margin-bottom: 4px;
}

.file-size {
  font-size: 0.9em;
  color: var(--el-text-color-secondary);
}

.settings-card {
  height: 100%;
}

.settings-form {
  width: 100%;
  min-width: 250px;
}

.slider-with-input {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
}

.quality-input,
.width-input {
  width: 100px;
}

:deep(.el-form-item__label) {
  line-height: 1.5;
  padding-bottom: 8px;
}

:deep(.el-form-item) {
  margin-bottom: 24px;
}

:deep(.el-slider) {
  flex: 1;
  min-width: 120px;
}

.mb-4 {
  margin-bottom: 1rem;
}
</style>
